using System;
using System.Collections.Generic;
using System.Text;

using KLATencor.CommonApp.Infrastructure.StateMachine;

using Syncfusion.Windows.Forms.Diagram;
using StateMachine_Graphical_Editor.Shapes;

namespace StateMachine_Graphical_Editor.Utils
{
    /// <summary>
    /// Provides methods for the state hierarchy analyzing
    /// </summary>
    public class GraphValidator
    {
        #region Members:

        private StateCollection rootSubStates;

        #endregion

        #region C'tor:

        public GraphValidator(StateCollection stateCollection)
        {
            rootSubStates = stateCollection;
        }

        #endregion

        #region Methods:

        /// <summary>
        ///Checks if the transition should be presented on diagram or not.
        ///<para>
        ///note: doesn't check for multiple transitions between 2 same states!! 
        /// see <see cref="GraphValidator.checkMultipleTransitions"/> for that issue.</para>
        /// </summary>
        /// <param name="transition"> the transition's name</param>
        /// <returns> true - if doesn't need to draw this transition 
        ///     <para>false - otherwise</para>
        /// </returns>
        public bool unnecessaryOutGoingTransition(Transition transition)
        {
            State src = rootSubStates.Find(transition.Source.Name);
            State dst = rootSubStates.Find(transition.Target.Name);
            
            //if one is null, then they don't have common ancestor 
            //(one of them is in parallel hierarchy)
            if (dst == null) 
            {
                return false;
            }

            return offspringAncestorReletion(src, dst);
        }

        /// <summary>
        ///Checks if the transition should be presented on diagram or not.
        ///<para>
        ///note: doesn't check for multiple transitions between 2 same states!! 
        /// see <see cref="GraphValidator.checkMultipleTransitions"/> for that issue.</para>
        /// </summary>
        /// <param name="transition"> the transition's name</param>
        /// <returns> true - if doesn't need to draw this transition 
        ///     <para>false - otherwise</para>
        /// </returns>
        public bool unnecessaryInGoingTransition(Transition transition)
        {
            State src = rootSubStates.Find(transition.Source.Name);
            State dst = rootSubStates.Find(transition.Target.Name);

            if (src == null)
            {
                //check if src is ana ancestor of dst:
                src = transition.Source.Substates.Find(dst.Name);
                if (src != null) //no need to show such transition
                    return true;
            }

            //check if transition was already drawn as an out-going one
            foreach (State state in rootSubStates)
            {
                if (state.Equals(transition.Source))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Check if transition is going from an ancestor to one of its offsprings
        /// </summary>
        /// <param name="state1"></param>
        /// <param name="state2"></param>
        /// <returns>true if one of state1, state2 is an ancestor of the other
        /// <para>false otherwise.</para></returns>
        private bool offspringAncestorReletion(State state1, State state2)
        {
            return 
                state1.Substates.Find(state2.Name)!=null ||
                state2.Substates.Find(state1.Name)!=null;
        }

        /// <summary>
        ///Checks for multiple transitions between same 2 states.  
        /// </summary>
        /// <param name="transition">Contains the source and target states to be checked</param>
        /// <param name="GTransitions">The GTransitions that already exist</param>
        /// <returns>The GTransition to be duplicated, or null if none</returns>
        public GTransition checkMultipleTransitions(Transition transition, NodeCollection GTransitions)
        {
            //get states from transition
            State src = rootSubStates.Find(transition.Source.Name);
            State dst = rootSubStates.Find(transition.Target.Name);

            if (src == null)
                src = transition.Source;
            if (dst == null)
                dst = transition.Target;

            //look for an existing transition between 2 states
            foreach (Node tmp in GTransitions)
            {
                //GTranstition gTrans = null;

                if (tmp is GTransition)
                {
                    GTransition gTrans = tmp as GTransition;

                    Node source = (Node)gTrans.FromNode;
                    Node target = (Node)gTrans.ToNode;

                    //check if duplicated:
                    //check if exactly both states already exist, 
                    //or if the target and/or source are offsprings of those states
                    GState gSource = source as GState;
                    GState gTarget = target as GState;

                    if (  (src.Name.Equals(source.Name) || (gSource!=null && gSource.LogicState.Substates.Find(src.Name)!=null)) &&
                          (dst.Name.Equals(target.Name) || (gTarget!=null && gTarget.LogicState.Substates.Find(dst.Name)!=null))  )
                    {
                        return gTrans;
                    }
                }
            }
            //if not found such transition
            return null;
        }

        //.
        //if one is an ancestor of the other - returns the ancestor's parent.
        //if machine is the common ancestor, returns null;
        /// <summary>
        /// Returns first common ancestor of both states.
        /// </summary>
        /// <param name="st1">state1</param>
        /// <param name="st2">state2</param>
        /// <returns>The state which is the lowest (in state hierarchy) and common ancestor
        /// <para>to st1 and st2.</para>
        /// <para>null if the common ancestor is the state machine root</para>
        /// </returns>
        public static State getFirstCommonAncestor(State st1, State st2)
        {
            //0. if one is null, then the common is the machine:
            if (st1 == null || st2 == null)
                return null;

            //1. if one is the ancestor of the other:
            if (st1.Substates.Find(st2.Name) != null)
                return st1;
            if (st2.Substates.Find(st1.Name) != null)
                return st2;

            return getFirstCommonAncestor(st1.Parent, st2.Parent); 
        }

        /// <summary>
        /// Returns the ancestor in this hierarchy level which is the parent of the desired state
        /// </summary>
        /// <param name="offspring">the state to find the ancestor to</param>
        /// <returns>the first ancestor of the offspring state</returns>
        public State getFirstAncestor(State offspring)
        {
            foreach (State state in rootSubStates)
            {
                //check if is directly in this hierarchy
                if (state.Equals(offspring))
                    return state;//.Parent;
                //check if ancestor in this hierarchy
                if (state.Substates.Find(offspring.Name) != null)
                    return state;
            }
            return null; //if in higher hierarchy
        }

        #endregion
    }
}
